export type ArrayPredicate<T> = (item: T, index: number, array: T[]) => boolean;

/**
 * 一个只读空数组
 */
export const EmptyArray: ReadonlyArray<any> = Object.freeze([]);

/**
 * 删除数组中的元素
 * @param array 数组
 * @param predicate 删除条件
 * @returns boolean/number breakOnMatchFirst=true时,若成功删除元素则返回该删除元素的索引,不成功返回-1；breakOnMatchFirst=false时返回布尔值
 */
export function deleteEl<T>(array: T[], predicate: ArrayPredicate<T>): boolean;

/**
 * 删除数组中的元素
 * @param array 数组
 * @param predicate 删除条件
 * @param breakOnMatchFirst 当匹配了一个元素后停止循环
 * @returns boolean/number breakOnMatchFirst=true时,若成功删除元素则返回该删除元素的索引,不成功返回-1；breakOnMatchFirst=false时返回布尔值
 */
export function deleteEl<T>(array: T[], predicate: ArrayPredicate<T>, breakOnMatchFirst: true): number;
export function deleteEl<T>(array: T[], predicate: ArrayPredicate<T>, breakOnMatchFirst = false): boolean | number {
  let flag = false;
  for (let i = array.length - 1; i >= 0; i--) {
    if (predicate(array[i], i, array)) {
      array.splice(i, 1);
      flag = true;
      if (breakOnMatchFirst) {
        return i;
      }
    }
  }
  return !flag && breakOnMatchFirst ? -1 : flag;
}

/**
 * 更新数组元素
 * @param array 数组
 * @param newEl 更新元素
 * @param predicate 元素索引查找函数
 */
export function updateEl<T>(array: T[], newEl: T, predicate: ArrayPredicate<T>): void {
  const index = array.findIndex(predicate);
  if (index !== -1) {
    array[index] = newEl;
  }
}

export function updateItem<T>(array: T[], predicate: ArrayPredicate<T>, update: (it: T) => void) {
  array.forEach((it, index) => {
    if (predicate(it, index, array)) {
      update(it);
    }
  });
}

export function pushOrRemoveEl<T>(array: T[], el: T, predicate?: ArrayPredicate<T>): void {
  const index = array.findIndex(predicate || (it => it === el));
  if (index === -1) {
    array.push(el);
  } else {
    array.splice(index, 1);
  }
}

/**
 * 从数组中采样
 * @param array 数组
 * @param samples 采样数量
 */
export function sampling<T>(array: T[], samples: number) {
  const len = array.length;
  if (len <= samples) {
    return array.slice();
  }
  const sliced: T[] = [];
  const gap = Math.floor(len / samples);
  for (let i = 0; i < len; i += gap) {
    sliced.push(array[i]);
  }
  return sliced;
}

export function mapSet<T, R>(set: Set<T>, func: (it: T, index: number, set: Set<T>) => R): R[] {
  const arr: R[] = [];
  let i = 0;
  for (let it of set) {
    arr.push(func(it, i++, set));
  }
  return arr;
}

export function numberArray2Object<T extends number>(array: T[]): Record<T, boolean> {
  const obj: Record<T, boolean> = Object.create(null);
  for (const it of array) {
    obj[it] = true;
  }
  return obj;
}

/**
 * 返回数组中的最大值
 * @param array
 * @param func
 */
export function max<T, R>(array: T[], func: (it: T, index: number, array: T[]) => R): R | undefined {
  const length = array.length;
  if (length === 0) {
    return undefined;
  }

  let ret: R = func(array[0], 0, array);
  for (let i = 1; i < length; i++) {
    const v = func(array[i], i, array);
    if (v > ret) {
      ret = v;
    }
  }
  return ret;
}

/**
 * 返回数组中的最小值
 * @param array
 * @param func
 */
export function min<T, R>(array: T[], func: (it: T, index: number, array: T[]) => R): R | undefined {
  const length = array.length;
  if (length === 0) {
    return undefined;
  }

  let ret: R = func(array[0], 0, array);
  for (let i = 1; i < length; i++) {
    const v = func(array[i], i, array);
    if (v < ret) {
      ret = v;
    }
  }
  return ret;
}

/**
 * 从数组中同时找出最大值和最小值
 * @param array
 * @param func
 */
export function findMinAndMax<T, R>(array: T[], func: (it: T, index: number, array: T[]) => R): [R | undefined, R | undefined] {
  if (array.length === 0) {
    return [undefined, undefined];
  }

  let min: R = func(array[0], 0, array);
  let max = min;

  const length = array.length;
  for (let i = 1; i < length; i++) {
    const v = func(array[i], i, array);
    if (v < min) {
      min = v;
    } else if (v > max) {
      max = v;
    }
  }

  return [min, max];
}

/**
 * 执行集合的map和filter
 * @param array 集合数组
 * @param func 函数返回false时过滤掉数据
 */
export function mapAndFilter<T, R>(array: T[], func: (item: T, index: number, array: T[]) => R | false): R[] {
  const list: R[] = [];
  for (let i = 0; i < array.length; i++) {
    const item = array[i];
    const ret = func(item, i, array);
    if (ret === false) {
      continue;
    }
    list.push(ret);
  }

  return list;
}
